package com.im.utils;

import com.im.config.JwtProperties;
import com.im.constant.RedisConstant;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * JWT 工具类（适配 JJWT 0.12.3，支持 Redis 进行 Token 状态管理）
 */
@Slf4j
@Component
public class TokenUtil {

    private final JwtProperties jwtProperties;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public static final String CLAIM_KEY_USERNAME = "username";
    public static final String CLAIM_KEY_CREATED = "created";
    public static final String CLAIM_KEY_PHONE = "phone";

    public TokenUtil(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
    }

    /**
     * 获取加密密钥（JJWT 0.12.3 要求 Key 不能是 String）
     */
    private Key getSigningKey() {
        return Keys.hmacShaKeyFor(jwtProperties.getAdminSecretKey().getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 生成 JWT Token
     */
    public String generateToken(Integer userId, String username,String phone) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, username);
        claims.put(CLAIM_KEY_PHONE, phone);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(userId, claims);
    }

    /**
     * 生成 JWT（带 userId）
     */
    private String generateToken(Integer userId, Map<String, Object> claims) {
        String token = Jwts.builder()
                .claims(claims)
                .subject(String.valueOf(userId))
                .expiration(generateExpirationDate())
                .signWith(getSigningKey())
                .compact();

        // Redis 存储 Token 状态，仅存 "1"，用于控制 Token 是否可用
        stringRedisTemplate.opsForValue().set(
                RedisConstant.TOKEN_PREFIX + userId, "1",
                jwtProperties.getAdminTtl(), TimeUnit.MILLISECONDS
        );

        return jwtProperties.getAdminTokenPrefix() + " " + token;
    }

    /**
     * 解析 JWT 获取 Claims
     */
    public Claims parseToken(String token) {
        try {
            if (token.startsWith(jwtProperties.getAdminTokenPrefix() + " ")) {
                token = token.replace(jwtProperties.getAdminTokenPrefix() + " ", "");
            }

            return Jwts.parser()
                    .setSigningKey(getSigningKey())
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        } catch (JwtException e) {
            log.error("❌ Token 解析失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 生成 Token 过期时间
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + jwtProperties.getAdminTtl());
    }

    /**
     * 获取 Token 里的 userId
     */
    public Integer getUserIdFromToken(String token) {
        Claims claims = parseToken(token);
        return claims != null ? Integer.valueOf(claims.getSubject()) : null;
    }

    /**
     * 获取 Token 里的 username
     */
    public String getUsernameFromToken(String token) {
        Claims claims = parseToken(token);
        return claims != null ? claims.get(CLAIM_KEY_USERNAME, String.class) : null;
    }

    /**
     * 校验 Token 是否有效（先解析 JWT，再查询 Redis 状态）
     */
    public boolean validateToken(String token) {
        Integer userId = getUserIdFromToken(token);
        if (userId == null) return false;

        // 检查 Redis 是否存储 Token 状态
        String tokenStatus = stringRedisTemplate.opsForValue().get(RedisConstant.TOKEN_PREFIX + userId);
        return tokenStatus != null && tokenStatus.equals("1") && !isTokenExpired(token);
    }

    /**
     * 刷新 Token（更新 `created` 时间）
     */
    public String refreshToken(String token) {
        Claims claims = parseToken(token);
        if (claims != null) {
            claims.put(CLAIM_KEY_CREATED, new Date());
            Integer userId = Integer.valueOf(claims.getSubject());
            return generateToken(userId, claims);
        }
        return null;
    }

    /**
     * 让 Token 失效（登出时调用）
     */
    public void invalidateToken(Integer userId) {
        stringRedisTemplate.delete(RedisConstant.TOKEN_PREFIX + userId);
    }

    /**
     * 判断 Token 是否过期
     */
    private boolean isTokenExpired(String token) {
        Date expiration = getExpiredDateFromToken(token);
        return expiration != null && expiration.before(new Date());
    }

    /**
     * 获取 Token 过期时间
     */
    private Date getExpiredDateFromToken(String token) {
        Claims claims = parseToken(token);
        return claims != null ? claims.getExpiration() : null;
    }
}
